package com.tuanzi.loan.workflow.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.activiti.engine.*;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.bpmn.behavior.ExclusiveGatewayActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.NoneEndEventActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.ParallelGatewayActivityBehavior;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.bpmn.helper.ClassDelegate;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.tuanzi.loan.business.entity.system.Role;
import com.tuanzi.loan.business.entity.system.SystemUser;
import com.tuanzi.loan.business.service.system.DictionaryService;
import com.tuanzi.loan.business.service.system.RoleService;
import com.tuanzi.loan.business.service.system.SystemUserService;
import com.tuanzi.loan.workflow.vo.*;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Ray
 * @date 2018/01/16
 */
@Slf4j
@Service
@Transactional
public class WorkflowBaseService {

	@Autowired
	protected ProcessEngine processEngine;
	@Autowired
	protected RepositoryService repositoryService;
	@Autowired
	protected RuntimeService runtimeService;
	@Autowired
	protected TaskService taskService;
	@Autowired
	protected IdentityService identityService;
	@Autowired
	protected HistoryService historyService;
	@Autowired
	protected SystemUserService systemUserService;
	@Autowired
	protected RoleService roleService;
	@Autowired
	protected DictionaryService dictionaryService;
	@Autowired
	private StartPageService startPageService;

	/**
	 * 查询指定流程实例的流程历史变量list
	 *
	 * @param processInstanceId
	 *            流程实例id
	 * @return 流程历史变量list
	 * @date 2016年9月19日 下午3:00:57
	 */
	public List<HistoricVariableInstance> queryHistoricVariableInstance(String processInstanceId) {
		if (StringUtils.isNotEmpty(processInstanceId)) {
			return historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
		}
		return new ArrayList<HistoricVariableInstance>();
	}

	public Long countProcInstByProcDefId(String procDefId) {
		return historyService.createHistoricProcessInstanceQuery().processDefinitionId(procDefId).count();
	}

	/**
	 * 获取历史任务的执行者
	 *
	 * @param processInstanceId
	 *            流程实例id
	 * @param step
	 *            第几个任务 usertask*
	 * @return
	 */
	public String getHistoricTaskAssignee(String processInstanceId, String step) {
		String assignee = "";
		if (StringUtils.isNotEmpty(processInstanceId)) {
			List<HistoricTaskInstance> historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByTaskCreateTime()
					.desc().list();
			String userTask = "usertask" + step;
			for (HistoricTaskInstance hti : historicTaskInstanceList) {
				if (userTask.equals(hti.getTaskDefinitionKey())) {
					assignee = hti.getAssignee();
					break;
				}
			}
		}

		return assignee;
	}

	protected TaskResponse structureTaskResponseFrom(HistoricTaskInstance historicTaskInstance) {
		TaskResponse taskResponse = new TaskResponse();
		BeanUtils.copyProperties(historicTaskInstance, taskResponse);

		taskResponse.setAssigneeName(systemUserService.findNameByAccount(historicTaskInstance.getAssignee()));
		taskResponse.setProcDefId(historicTaskInstance.getProcessDefinitionId());
		taskResponse.setProcInstId(historicTaskInstance.getProcessInstanceId());
		taskResponse.setTaskName(historicTaskInstance.getName());
		return taskResponse;
	}

	private AssigneeResponse structureAssigneeResponse(String activityId, String assignee, String assigneeName, AssigneeType assigneeType) {
		AssigneeResponse assigneeResponse = new AssigneeResponse();
		assigneeResponse.setActivityId(activityId);
		assigneeResponse.setAssignee(assignee);
		assigneeResponse.setAssigneeName(assigneeName);
		assigneeResponse.setAssigneeType(assigneeType);
		return assigneeResponse;
	}

	protected ActivityResponse mappingUserTaskActivityImpl2ActivityResponse(ActivityImpl activityImpl, Map<String, Object> variables) {

		if (!isUserTask(activityImpl)) {
			return null;
		}

		TaskDefinition taskDefinition = (TaskDefinition) activityImpl.getProperty("taskDefinition");
		List<AssigneeResponse> assigneeResponses = Lists.newArrayList();

		Expression assigneeExp = taskDefinition.getAssigneeExpression();
		String assigneeVariable = null;
		AssigneeResponse assigneeVariableResponse = null;
		if (null != assigneeExp) {
			if (assigneeExp.getExpressionText().indexOf("$") != -1) {
				assigneeVariable = assigneeExp.getExpressionText();
				if (MapUtils.isNotEmpty(variables)) {
					Object account = variables.get(StringUtils.replace(StringUtils.replace(assigneeVariable, "${", ""), "}", ""));
					if (account != null) {
						SystemUser systemUser = systemUserService.findOneByAccount(account.toString());
						assigneeResponses.add(structureAssigneeResponse(taskDefinition.getKey(), systemUser.getAccount(), systemUser.getName(), AssigneeType.USER));
					}
				}
			} else {
				SystemUser systemUser = systemUserService.findOneByAccount(assigneeExp.getExpressionText());

				if (systemUser == null) {
					log.error("获得users执行者，帐号对应的用户不存在！account : {}", assigneeExp.getExpressionText());
				} else {
					assigneeResponses.add(structureAssigneeResponse(taskDefinition.getKey(), systemUser.getAccount(), systemUser.getName(), AssigneeType.USER));
				}
			}
		}

		// 获得users执行者
		Set<Expression> userExpSet = taskDefinition.getCandidateUserIdExpressions();
		if (CollectionUtils.isNotEmpty(userExpSet)) {
			for (Expression userExp : userExpSet) {
				SystemUser systemUser = systemUserService.findOneByAccount(userExp.getExpressionText());

				if (systemUser == null) {
					log.error("获得users执行者，帐号对应的用户不存在！account=" + userExp.getExpressionText());
				} else {
					assigneeResponses.add(structureAssigneeResponse(taskDefinition.getKey(), systemUser.getAccount(), systemUser.getName(), AssigneeType.USER));
				}
			}
		}

		// 获得group执行者
		Set<Expression> groupExpSet = taskDefinition.getCandidateGroupIdExpressions();
		if (groupExpSet != null && !groupExpSet.isEmpty()) {
			for (Expression expression : groupExpSet) {
				String groupExp = expression.getExpressionText();

				if (groupExp.startsWith(AssigneeType.ROLE.name())) {
					String roleCode = groupExp.substring(groupExp.indexOf("_") + 1);
					Role role = roleService.findByCode(roleCode);
					if (null != role) {
						assigneeResponses.add(structureAssigneeResponse(taskDefinition.getKey(), role.getCode(), role.getName(), AssigneeType.ROLE));
					}

				}
			}
		}

		// 加载flow信息
		List<RouterResponse> preRouter = Lists.newArrayList();
		List<RouterResponse> postRouter = Lists.newArrayList();

		List<PvmTransition> incomePvmTran = activityImpl.getIncomingTransitions();
		List<PvmTransition> outgoingPvmTran = activityImpl.getOutgoingTransitions();

		for (PvmTransition pvmTransition : incomePvmTran) {
			mappingPvm2Router(preRouter, pvmTransition, null);
		}

		for (PvmTransition pvmTransition : outgoingPvmTran) {

			ActivityImpl act = (ActivityImpl) pvmTransition.getDestination();
			boolean isPlGateway = act.getActivityBehavior() instanceof ParallelGatewayActivityBehavior;
			// 判断流出节点是否分支节点
			if (act.getActivityBehavior() instanceof ExclusiveGatewayActivityBehavior) {
				ActivityImpl act2 = act;
				while (act2.getActivityBehavior() instanceof ExclusiveGatewayActivityBehavior) {
					pvmTransition = this.getNextPvmTransition(act2, variables);
					act2 = (ActivityImpl) pvmTransition.getDestination();
				}
			} else if (act.getActivityBehavior() instanceof ClassDelegate) {
				pvmTransition = this.getNextPvmTransition4ServiceTask(act, variables);
			}
			if (pvmTransition != null && !isPlGateway) {
				mappingPvm2Router(postRouter, pvmTransition, act);
			}
			// 判断流程节点是否并行网关分支节点
			// @auth huangza
			if (isPlGateway) {
				List<PvmTransition> pvmTransitions = this.getNextPvmTransitions(act, variables);
				if (pvmTransitions != null) {
					mappingPvms2Router(postRouter, pvmTransitions, act);
				}
			}

		}

		ActivityResponse activityResponse = new ActivityResponse();
		activityResponse.setId(activityImpl.getId());
		activityResponse.setActivityId(taskDefinition.getKey());
		activityResponse.setActivityName(activityImpl.getProperty("name").toString());
		activityResponse.setActivityType(activityImpl.getProperty("type").toString());
		activityResponse.setProcessDefinitionId(activityImpl.getProcessDefinition().getId());
		activityResponse.setAssignees(assigneeResponses);
		activityResponse.setAssigneeVariable(assigneeVariable);
		activityResponse.setAssigneeVariableResponse(assigneeVariableResponse);
		activityResponse.setPreRouters(preRouter);
		activityResponse.setPostRouters(postRouter);
		activityResponse.setUserEvent(true);
		return activityResponse;

	}

	//
	// /**
	// * 将activityImpl对象映射到ActivityVO
	// *
	// * @param activityImpl
	// * @return
	// */
	protected ActivityResponse mappingServiceTaskActivityImpl2ActivityResponse(ActivityImpl activityImpl, Map<String, Object> variables) {
		ActivityResponse activityResponse = null;

		if (!(activityImpl.getActivityBehavior() instanceof UserTaskActivityBehavior)) {
			activityResponse = new ActivityResponse();
			activityResponse.setId(activityImpl.getId());
			activityResponse.setActivityId(activityImpl.getId());
			activityResponse.setActivityName(activityImpl.getProperty("name").toString());
			activityResponse.setActivityType(activityImpl.getProperty("type").toString());
			activityResponse.setProcessDefinitionId(activityImpl.getProcessDefinition().getId());
			activityResponse.setUserEvent(false);
			activityResponse.setAssignees(Lists.newArrayList());
			activityResponse.setAssigneeVariable(null);
			activityResponse.setPreRouters(null);
			activityResponse.setPostRouters(null);
		}

		return activityResponse;
	}

	//
	// /**
	// * 功能：将ProcessDefinition转换为ProcessDefinitionVO
	// *
	// * @param procDef
	// * @return
	// * @throws IllegalAccessException
	// * @throws InvocationTargetException
	// */
	protected ProcessDefinitionResponse structureProcessDefinitionResponseFrom(ProcessDefinition processDefinition) {
		Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(processDefinition.getDeploymentId()).singleResult();

		ProcessDefinitionResponse response = new ProcessDefinitionResponse();
		BeanUtils.copyProperties(processDefinition, response);

		if (deployment != null) {
			response.setDeploymentId(deployment.getId());
			response.setDeploymentTime(deployment.getDeploymentTime());
			response.setProcInstCount(countProcInstByProcDefId(processDefinition.getId()));
		}

		return response;
	}

	protected List<ProcessDefinitionResponse> structureProcessDefinitionResponseFrom(List<ProcessDefinition> processDefinitions) {
		return processDefinitions.stream().map(this::structureProcessDefinitionResponseFrom).collect(Collectors.toList());
	}

	//
	/**
	 * 职责：获取业务种类数据字典
	 *
	 * 业务种类id
	 * 
	 * @return
	 */
	// protected Dic getCategoryDic(String categoryId) {
	// BaseResponse<> dicResponse = dicService.getDicByDicCodeAndDicDataCode("YWLX",
	// categoryId);
	// return dicResponse.getResult().getData();
	// }
	//
	// /**
	// * 将activityImpl对象映射到ActivityVO
	// *
	// * @param activityImpl
	// * @return
	// */
	// protected ActivityVO mappingActivityImpl2ActivityVO2(ActivityImpl
	// activityImpl, Map<String, Object> variables) {
	//
	// // 加载flow信息
	// List<RouterVO> preRouter = new ArrayList<>();
	// List<RouterVO> postRouter = new ArrayList<>();
	// List<PvmTransition> incomePvmTran = activityImpl.getIncomingTransitions();
	// List<PvmTransition> outgoingPvmTran = activityImpl.getOutgoingTransitions();
	// for (PvmTransition pvmTransition : incomePvmTran) {
	// mappingPvm2Router(preRouter, pvmTransition, null);
	// }
	// for (PvmTransition pvmTransition : outgoingPvmTran) {
	// ActivityImpl act = (ActivityImpl) pvmTransition.getDestination();
	// boolean isPlGateway = act.getActivityBehavior() instanceof
	// ParallelGatewayActivityBehavior;
	// // 判断流出节点是否分支节点
	// if (act.getActivityBehavior() instanceof ExclusiveGatewayActivityBehavior) {
	// pvmTransition = this.getNextPvmTransition(act, variables);
	// }
	// if (pvmTransition != null && !isPlGateway) {
	// mappingPvm2Router(postRouter, pvmTransition, act);
	// }
	// // 判断流程节点是否并行网关分支节点
	// // @auth huangza
	// if (isPlGateway) {
	// List<PvmTransition> pvmTransitions = this.getNextPvmTransitions(act,
	// variables);
	// if (pvmTransitions != null) {
	// mappingPvms2Router(postRouter, pvmTransitions, act);
	// }
	// }
	//
	// }
	//
	// ActivityVO actVO = new ActivityVO();
	// actVO.setId(activityImpl.getId());
	// actVO.setActivityName(activityImpl.getProperty("name").toString());
	// actVO.setActivityType(activityImpl.getProperty("type").toString());
	// actVO.setProcessDefinitionId(activityImpl.getProcessDefinition().getId());
	// actVO.setPreRouters(preRouter);
	// actVO.setPostRouters(postRouter);
	// return actVO;
	// }
	//
	// /**
	// * 功能：将pvm向量转换为routerVO集合
	// *
	// * @param preRouter
	// * @param pvmTransition
	// */
	private void mappingPvm2Router(List<RouterResponse> preRouter, PvmTransition pvmTransition, ActivityImpl act) {
		ActivityImpl pvmActivityImpl = (ActivityImpl) pvmTransition.getDestination();
		String flowId = pvmTransition.getId();
		String flowName = (String) pvmTransition.getProperty("name");
		String toActivityId = pvmActivityImpl.getId();
		String toActivityName = (String) pvmActivityImpl.getProperty("name");

		// 如果flow的名称为空，则用节点名称代替
		if (StringUtils.isBlank(flowName)) {
			flowName = toActivityName;
		}

		RouterResponse routerResponse = new RouterResponse();
		routerResponse.setFlowId(flowId);
		routerResponse.setFlowId(flowName);
		routerResponse.setToActivityId(toActivityId);
		routerResponse.setToActivityName(toActivityName);

		// 设置前置分支节点
		if (act != null) {
			if (act.getActivityBehavior() instanceof ExclusiveGatewayActivityBehavior || act.getActivityBehavior() instanceof ParallelGatewayActivityBehavior
					|| act.getActivityBehavior() instanceof ClassDelegate)
				routerResponse.setPreExclusiveGatewayId(act.getId());
		}

		preRouter.add(routerResponse);
	}

	/**
	 * 功能：获取下一步的流程线（兼容分支流程）
	 *
	 * @param activityImpl
	 * @param variables
	 * @return
	 */
	private PvmTransition getNextPvmTransition(ActivityImpl activityImpl, Map<String, Object> variables) {
		List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
		PvmTransition targetPvmTransition = null;

		for (PvmTransition tr : outTransitions) {
			if (tr.getProperty("conditionText") != null && variables != null) {
				String varName = null;
				Object varValue = null;
				Set<String> varSet = variables.keySet();
				String conText = (String) tr.getProperty("conditionText");
				for (String var : varSet) {
					if (!"outcome".equals(var) && conText.indexOf(var) != -1) {
						varName = var;
						varValue = variables.get(var);
					}
				}
				if (varName != null && varValue != null) {
					ExpressionFactory factory = new ExpressionFactoryImpl();
					SimpleContext context = new SimpleContext();
					context.setVariable(varName, factory.createValueExpression(varValue, String.class));
					ValueExpression e = factory.createValueExpression(context, (String) tr.getProperty("conditionText"), boolean.class);
					if (((Boolean) e.getValue(context)).booleanValue() == false) {
						// 过滤不匹配的分支
						continue;
					}
				}
			}

			targetPvmTransition = tr;
		}

		return targetPvmTransition;
	}

	/**
	 * 功能：获取下一步的流程线（兼容ServiceTask） 注意：非最佳实现方案！
	 *
	 * @param activityImpl
	 * @param variables
	 * @return
	 */
	private PvmTransition getNextPvmTransition4ServiceTask(ActivityImpl activityImpl, Map<String, Object> variables) {
		List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
		PvmTransition targetPvmTransition = null;

		for (PvmTransition tr : outTransitions) {
			// 优先获取下一个任务是usertask类型
			ActivityImpl act = (ActivityImpl) tr.getDestination();
			if (act.getActivityBehavior() instanceof UserTaskActivityBehavior) {
				targetPvmTransition = tr;
				break;
			}
		}
		if (targetPvmTransition == null) {
			for (PvmTransition tr : outTransitions) {
				// 再次获取下一个任务是endevent类型
				ActivityImpl act = (ActivityImpl) tr.getDestination();
				if (act.getActivityBehavior() instanceof NoneEndEventActivityBehavior) {
					// 注意：假如有多个结束节点，优先获取排序靠前的
					targetPvmTransition = tr;
					break;
				}
			}
		}

		return targetPvmTransition;
	}

	/**
	 * 功能：获取下一步的流程线（兼容并行网关分支流程）
	 *
	 * @author huangza
	 * @param activityImpl
	 * @param variables
	 * @return
	 */
	private List<PvmTransition> getNextPvmTransitions(ActivityImpl activityImpl, Map<String, Object> variables) {
		List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
		List<PvmTransition> targetPvmTransitions = new ArrayList<PvmTransition>();

		for (PvmTransition tr : outTransitions) {
			// 获取线路的终点节点
			// logger.info(tr.getId() + "," + tr.getProperty("name") + "," +
			// tr.getProperty("condition") + "," + tr.getProperty("conditionText"));
			if (tr.getProperty("conditionText") != null && variables != null) {
				// 根据流程变量判断分支节点
				String varName = null;
				Object varValue = null;
				Set<String> varSet = variables.keySet();
				String conText = (String) tr.getProperty("conditionText");
				for (String var : varSet) {
					if (!"outcome".equals(var) && conText.indexOf(var) != -1) {
						// 表达式中包含变量
						varName = var;
						varValue = variables.get(var);
					}
				}
				if (varName != null && varValue != null) {
					ExpressionFactory factory = new ExpressionFactoryImpl();
					SimpleContext context = new SimpleContext();
					context.setVariable(varName, factory.createValueExpression(varValue, String.class));
					ValueExpression e = factory.createValueExpression(context, (String) tr.getProperty("conditionText"), boolean.class);
					if (((Boolean) e.getValue(context)).booleanValue() == false) {
						// 过滤不匹配的分支
						continue;
					}
				}
			}

			targetPvmTransitions.add(tr);
		}

		return targetPvmTransitions;
	}

	private void mappingPvms2Router(List<RouterResponse> preRouter, List<PvmTransition> pvmTransitions, ActivityImpl act) {
		for (PvmTransition pvmTransition : pvmTransitions) {
			this.mappingPvm2Router(preRouter, pvmTransition, act);
		}
	}

	protected boolean isUserTask(ActivityImpl activityImpl) {
		return activityImpl.getProperty("taskDefinition") != null;
	}

}
